package vip.liux.backend.infrastructure.excel.handler;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;
import vip.liux.backend.infrastructure.excel.ExportContext;
import vip.liux.backend.infrastructure.excel.converters.DateStringConverter;
import vip.liux.backend.infrastructure.excel.converters.LocalDateStringConverter;
import vip.liux.backend.infrastructure.excel.converters.LocalDateTimeStringConverter;
import vip.liux.backend.infrastructure.excel.enhance.DefaultWriterBuilderEnhancer;
import vip.liux.backend.infrastructure.excel.enhance.WriterBuilderEnhancer;
import vip.liux.backend.infrastructure.excel.exception.ExcelException;
import vip.liux.backend.infrastructure.excel.head.HeadGenerator;
import vip.liux.backend.infrastructure.excel.head.HeadMeta;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Modifier;
import java.util.List;

/**
 * 抽象 Sheet 写出处理器，基于 ExportContext 配置
 */
public abstract class AbstractSheetWriteHandler implements SheetWriteHandler{

    @Setter
    private List<Converter<?>> converterProvider;
    @Setter
    private WriterBuilderEnhancer excelWriterBuilderEnhance = new DefaultWriterBuilderEnhancer();

    @Override
    public void check(ExportContext exportContext) {
        if (exportContext.getSheets() == null || exportContext.getSheets().isEmpty()) {
            throw new ExcelException("ExportContext sheet 配置不合法");
        }
    }

    @Override
    public void export(Object o, OutputStream outputStream, ExportContext exportContext) {
        Assert.notNull(o, "导出数据对象不能为空");
        Assert.notNull(outputStream, "输出流不能为空");
        Assert.notNull(exportContext, "导出上下文配置不能为空");
        check(exportContext);
        write(o, outputStream, exportContext);
    }

    /**
     * 通用的获取ExcelWriter方法
     *
     * @param outputStream  输出流
     * @param exportContext 导出上下文配置
     * @return ExcelWriter
     */
    public ExcelWriter getExcelWriter(OutputStream outputStream, ExportContext exportContext) {
        Assert.notNull(outputStream, "输出流不能为空");
        Assert.notNull(exportContext, "导出上下文配置不能为空");
        ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream)
                .registerConverter(LocalDateStringConverter.INSTANCE)
                .registerConverter(LocalDateTimeStringConverter.INSTANCE)
                .registerConverter(DateStringConverter.INSTANCE)
                .autoCloseStream(true)
                .inMemory(exportContext.isInMemory());

        if (StringUtils.isNotBlank(exportContext.getPassword())) {
            writerBuilder.password(exportContext.getPassword());
        }
        if (exportContext.getIncludes() != null && !exportContext.getIncludes().isEmpty()) {
            writerBuilder.includeColumnFieldNames(exportContext.getIncludes());
        }
        if (exportContext.getExcludes() != null && !exportContext.getExcludes().isEmpty()) {
            writerBuilder.excludeColumnFieldNames(exportContext.getExcludes());
        }
        if (exportContext.getWriteHandlers() != null) {
            for (Class<? extends WriteHandler> clazz : exportContext.getWriteHandlers()) {
                writerBuilder.registerWriteHandler(BeanUtils.instantiateClass(clazz));
            }
        }
        registerCustomConverter(writerBuilder);
        if (exportContext.getConverter() != null) {
            for (Class<? extends Converter<?>> clazz : exportContext.getConverter()) {
                writerBuilder.registerConverter(BeanUtils.instantiateClass(clazz));
            }
        }

        InputStream inputStream = null;
        try {
            if (StringUtils.isNotBlank(exportContext.getLocalTemplate())) {
                ClassPathResource classPathResource = new ClassPathResource(exportContext.getLocalTemplate());
                inputStream = classPathResource.getInputStream();
            } else if (exportContext.getRemoteTemplate() != null && StringUtils.isNotBlank(exportContext.getRemoteTemplateName())) {
                // inputStream = this.applicationContext.getBean(exportContext.getRemoteTemplate())
                //         .getTemplateInputStreamByName(exportContext.getRemoteTemplateName());
                throw new UnsupportedOperationException("远程模板导入未实现");
            }
            if (inputStream != null) {
                writerBuilder.withTemplate(inputStream);
            }
        } catch (IOException e) {
            throw new ExcelException("加载模板时发生异常");
        }

        writerBuilder = excelWriterBuilderEnhance.enhanceExcel(writerBuilder, outputStream, exportContext);

        return writerBuilder.build();
    }

    /**
     * 自定义注入转换器 如果有需要，子类自己重写
     */
    public void registerCustomConverter(ExcelWriterBuilder builder) {
        if (CollectionUtils.isNotEmpty(converterProvider)) {
            converterProvider.forEach(builder::registerConverter);
        }
    }

    /**
     * 获取 WriteSheet 对象
     *
     * @param sheet                 sheet 配置
     * @param dataClass             数据类型
     * @param useTemplate           是否模板导出
     * @param bookHeadEnhancerClass 头生成器
     * @return WriteSheet
     */
    public WriteSheet sheet(ExportContext.Sheet sheet, Class<?> dataClass, Boolean useTemplate,
                            Class<? extends HeadGenerator> bookHeadEnhancerClass) {
        String sheetName = sheet.getSheetName();
        ExcelWriterSheetBuilder writerSheetBuilder = Boolean.TRUE.equals(useTemplate)
                ? EasyExcel.writerSheet()
                : EasyExcel.writerSheet(sheetName);

        Class<? extends HeadGenerator> headGenerateClass = null;
        if (isNotInterface(sheet.getHeadGenerateClass())) {
            headGenerateClass = sheet.getHeadGenerateClass();
        } else if (isNotInterface(bookHeadEnhancerClass)) {
            headGenerateClass = bookHeadEnhancerClass;
        }
        if (headGenerateClass != null) {
            fillCustomHeadInfo(dataClass, headGenerateClass, writerSheetBuilder);
        } else if (dataClass != null) {
            writerSheetBuilder.head(dataClass);
            if (sheet.getExcludes() != null && !sheet.getExcludes().isEmpty()) {
                writerSheetBuilder.excludeColumnFieldNames(sheet.getExcludes());
            }
            if (sheet.getIncludes() != null && !sheet.getIncludes().isEmpty()) {
                writerSheetBuilder.includeColumnFieldNames(sheet.getIncludes());
            }
        }

        writerSheetBuilder = excelWriterBuilderEnhance.enhanceSheet(writerSheetBuilder, null, sheetName, dataClass, headGenerateClass);

        return writerSheetBuilder.build();
    }

    public void fillCustomHeadInfo(Class<?> dataClass, Class<? extends HeadGenerator> headEnhancerClass,
                                    ExcelWriterSheetBuilder writerSheetBuilder) {
        Assert.notNull(dataClass, "数据类不能为空");
        Assert.notNull(headEnhancerClass, "头生成器类不能为空");
        HeadGenerator headGenerator = BeanUtils.instantiateClass(headEnhancerClass);
        Assert.notNull(headGenerator, "The header generated bean does not exist.");
        HeadMeta head = headGenerator.head(dataClass);
        writerSheetBuilder.head(head.getHead());
        writerSheetBuilder.excludeColumnFieldNames(head.getIgnoreHeadFields());
    }

    /**
     * 是否为具体类
     */
    public boolean isNotInterface(Class<? extends HeadGenerator> headGeneratorClass) {
        return headGeneratorClass != null && !headGeneratorClass.isInterface()
                && !Modifier.isAbstract(headGeneratorClass.getModifiers());
    }
}
